package serialization

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"reflect"
)

// 基础数据类型枚举
const (
	TYPE_INT = iota
	TYPE_BOOL
	TYPE_FLOAT32
	TYPE_FLOAT64
	TYPE_STRING
)

// 魔数
var MAGIC = [...]byte{37, 61, 57, 23, 111}

// 整型转为字节
func IntToByte(i int) []byte {
	x := int64(i)
	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

// 字节转整型
func ByteToInt(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)
	var x int64
	binary.Read(bytesBuffer, binary.BigEndian, &x)
	return int(x)
}

// 对一批数据执行序列化
func MarshalArguments(arguments ...any) ([]byte, error) {
	types := make([]byte, 0, len(arguments)) // 存放每个参数的数据类型
	lens := make([]int, 0, len(arguments))   // 存放每个参数的长度
	buf := make([]byte, 0, 256)              // 用来保存序列化后的结果
	buffer := bytes.NewBuffer(buf)
	// 对数据进行序列化
	for i, arg := range arguments {
		switch v := arg.(type) {
		case bool:
			// 如果参数的长度是固定的，都可以通过 binary.Write 将其转换为字节
			err := binary.Write(buffer, binary.BigEndian, v)
			if err != nil {
				return nil, fmt.Errorf("序列化第 %d 个参数失败，%v, 数据类型为 %s", i, err, reflect.TypeOf(arg).Name())
			}
			types = append(types, TYPE_BOOL)
			lens = append(lens, 1) // bool 长度为1
		case float32:
			err := binary.Write(buffer, binary.BigEndian, v)
			if err != nil {
				return nil, fmt.Errorf("序列化第 %d 个参数失败，%v, 数据类型为 %s", i, err, reflect.TypeOf(arg).Name())
			}
			types = append(types, TYPE_FLOAT32)
			lens = append(lens, 4) // float32 长度为4
		case float64:
			err := binary.Write(buffer, binary.BigEndian, v)
			if err != nil {
				return nil, fmt.Errorf("序列化第 %d 个参数失败，%v, 数据类型为 %s", i, err, reflect.TypeOf(arg).Name())
			}
			types = append(types, TYPE_FLOAT64)
			lens = append(lens, 8) // float64 长度是8
		case int:
			// 整型数据长度不固定，32位设备长度需要转换为int32()64位设备需要转换为int64()
			err := binary.Write(buffer, binary.BigEndian, int64(v))
			if err != nil {
				return nil, fmt.Errorf("序列化第 %d 个参数失败，%v, 数据类型为 %s", i, err, reflect.TypeOf(arg).Name())
			}
			types = append(types, TYPE_INT)
			lens = append(lens, 8) // int 长度是8
		case string:
			// 字符串类型需要使用 WriteString 来进行转换
			_, err := buffer.WriteString(v)
			if err != nil {
				return nil, fmt.Errorf("序列化第 %d 个参数失败，%v, 数据类型为 %s", i, err, reflect.TypeOf(arg).Name())
			}
			types = append(types, TYPE_STRING)
			lens = append(lens, len(v)) // string 长度需要计算
		default:
			return nil, fmt.Errorf("序列化第 %d 个参数失败, 暂不支持的数据类型 %s", i, reflect.TypeOf(arg).Name())
		}
	}
	// 拼接魔数、数据个数、数据类型、数据长度
	result := make([]byte, 0, len(MAGIC)+8+len(types)+8*len(types)+buffer.Len()) // 声明结果切片容量
	writer := bytes.NewBuffer(result)                                            // 构建byte
	writer.Write(MAGIC[:])                                                       //填入魔数
	writer.Write(IntToByte(len(types)))                                          // 填入参数个数
	writer.Write(types)                                                          // 填入参数类型
	for _, l := range lens {
		writer.Write(IntToByte(l)) // 填入参数长度
	}
	writer.Write(buffer.Bytes()) // 填入参数内容
	return writer.Bytes(), nil   // 返回结果
}

// 对一批数据执行反序列化
func UnmarshalArguments(stream []byte) ([]any, error) {
	if len(stream) < len(MAGIC)+8 {
		// 字节流至少不能少于魔数+数据长度 即使参数是空也需要这些参数
		return nil, fmt.Errorf("数据长度不足: %d", len(stream))
	}

	if !bytes.Equal(MAGIC[:], stream[:len(MAGIC)]) {
		return nil, fmt.Errorf("魔数校验失败：%v", stream[:len(MAGIC)])
	}

	pos := len(MAGIC)                   // 初始位置为魔数的长度
	n := ByteToInt(stream[pos : pos+8]) // 取出参数个数
	pos += 8                            // 参数个数为int int 长度为8
	if n <= 0 {
		// 如果参数个数为0则直接返回空
		return nil, nil
	}
	// 后续每截取一次数据就要校验数据长度，以免发生内存越界导致 panic 终止进程
	if len(stream) <= len(MAGIC)+8+n+8*n {
		return nil, fmt.Errorf("数据流长度太短: %d , 有 %d 个参数", len(stream), n)
	}

	types := make([]byte, 0, n) // 切片保存每个参数类型
	for i := 0; i < n; i++ {
		types = append(types, stream[pos])
		pos += 1
	}
	totalLen := 0             // 所有参数总长度
	lens := make([]int, 0, n) // 切片保存每个参数长度
	for i := 0; i < n; i++ {
		l := ByteToInt(stream[pos : pos+8]) // 长度为int类型，默认为8
		lens = append(lens, l)
		totalLen += l
		pos += 8 // 长度为 int 类型，默认为8
	}

	if len(stream[pos:]) < totalLen {
		return nil, fmt.Errorf("数据长度不足为: %d , 期望长度为: %d", len(stream), pos+totalLen)
	}

	arguments := make([]any, 0, n) // 参数内容

	for i := 0; i < n; i++ {
		t := types[i]
		l := lens[i]
		bytesBuffer := bytes.NewBuffer(stream[pos : pos+l])
		var arg any
		switch t {
		case TYPE_BOOL:
			var x bool
			err := binary.Read(bytesBuffer, binary.BigEndian, &x)
			if err != nil {
				return nil, fmt.Errorf("反序列化第 %d 个参数失败, %v, 数据类型: %d", i, err, t)
			}
			arg = x
		case TYPE_FLOAT32:
			var x float32
			err := binary.Read(bytesBuffer, binary.BigEndian, &x)
			if err != nil {
				return nil, fmt.Errorf("反序列化第 %d 个参数失败, %v, 数据类型: %d", i, err, t)
			}
			arg = x
		case TYPE_FLOAT64:
			var x float64
			err := binary.Read(bytesBuffer, binary.BigEndian, &x)
			if err != nil {
				return nil, fmt.Errorf("反序列化第 %d 个参数失败, %v, 数据类型: %d", i, err, t)
			}
			arg = x
		case TYPE_INT:
			x := ByteToInt(stream[pos : pos+l])
			arg = x
		case TYPE_STRING:
			x := string(stream[pos : pos+l])
			arg = x
		default:
			return nil, fmt.Errorf("反序列化第 %d 个参数失败, 暂不支持的数据类型: %d", i, t)
		}
		pos += l                           // 当前位置递增
		arguments = append(arguments, arg) //结果追加到切片
	}
	return arguments, nil // 结果返回
}

type MySerializer struct{}

func (s MySerializer) Marshal(object any) ([]byte, error) {
	arguments := make([]any, 0, 10)
	typ := reflect.TypeOf(object) // 将空接口转换为 type
	v := reflect.ValueOf(object)  // 将空接口转换为 value

	for i := 0; i < v.NumField(); i++ {
		if !typ.Field(i).IsExported() { // 序列化前判断是否存在不可导出成员
			continue
		}
		arguments = append(arguments, v.Field(i).Interface()) // 可导出内容通过 .Interface 转换为接口
	}
	stream, err := MarshalArguments(arguments...) // 将切片转为不定长参数并执行序列化
	return stream, err                            // 返回序列化后的切片
}

// 将 stream 字节流转换为 object结构体对象
func (s MySerializer) Unmarshal(stream []byte, object any) error {
	typ := reflect.TypeOf(object)  // 获取传入对象的类型
	val := reflect.ValueOf(object) // 获取传入对象的值
	if typ.Kind() != reflect.Ptr {
		// 传入对象是否包含指针
		return errors.New("必须传入指针")
	}
	typ = typ.Elem() // 获取指针指向的元素类型
	val = val.Elem() // 获取指针指向的元素值
	if typ.Kind() != reflect.Struct {
		// 检查元素类型是否为结构体
		return errors.New("必须传结构体的指针")
	}
	argumens, err := UnmarshalArguments(stream) // 调用 UnmarshalArguments 函数将字节流 stream 解析为具体的参数列表 argumens
	if err != nil {
		return err
	}
	j := 0
	for i := 0; i < typ.NumField(); i++ {
		if !typ.Field(i).IsExported() { // 判断成员变量是否不可导出
			continue
		}
		arg := argumens[j]
		j++
		switch typ.Field(i).Type.Kind() {
		case reflect.Int:
			if v, ok := arg.(int); ok { // 通过断言将空接口转为具体数据类型
				val.Field(i).SetInt(int64(v)) // 通过反射赋值给结构体成员变量
			} else {
				return fmt.Errorf("第%d个参数类型不一致, 期待为 int", i)
			}
		case reflect.Float32:
			if v, ok := arg.(float32); ok {
				val.Field(i).SetFloat(float64(v))
			} else {
				return fmt.Errorf("第%d个参数类型不一致, 期待为 float32", i)
			}
		case reflect.Float64:
			if v, ok := arg.(float64); ok {
				val.Field(i).SetFloat(v)
			} else {
				return fmt.Errorf("第%d个参数类型不一致, 期待为 float64", i)
			}
		case reflect.Bool:
			if v, ok := arg.(bool); ok {
				val.Field(i).SetBool(v)
			} else {
				return fmt.Errorf("第%d个参数类型不一致, 期待为 bool", i)
			}
		case reflect.String:
			if v, ok := arg.(string); ok {
				val.Field(i).SetString(v)
			} else {
				return fmt.Errorf("第%d个参数类型不一致, 期待为 string", i)
			}
		default:
			return fmt.Errorf("不支持的数据类型 %s", val.Field(i).Kind().String())
		}
	}
	return nil
}
